# -*- coding: utf-8 -*-
"""
轮动策略框架 | 邢不行 | 2024分享会
author: 邢不行
微信: xbx6660
"""
import pandas as pd
from glob import glob
import sys
from joblib import Parallel, delayed
from tqdm import tqdm
from Config import *
import Functions as Func
import Evaluate as Eva

pd.set_option('display.max_rows', 1000)
pd.set_option('expand_frame_repr', False)  # 当列太多时不换行


# 动态读取config里面配置的shift_name脚本
Shift = __import__('shift.%s' % shift_name, fromlist=('',))

# =====取出RShift中配置的变量
# 从Shift中取出 因子列表factor_list
factor_list = Shift.factor_list
# 从Shift中取出 回测持仓周期hold_period
hold_period = Shift.hold_period
# 从Shift中取出 策略名stg_name
stg_name = Shift.stg_name

# 设定想遍历的offset
offsets = list(range(0, int(hold_period[:-1])))
# 获取当前环境下的python解释器
python_exe = sys.executable


def ergodic_back_test(_offset):
    os.system(f'%s "{root_path}/program/1_轮动策略回测.py" %s' % (python_exe, _offset))


# 多线程执行还是单线程执行
multiple_process = False
if multiple_process:
    Parallel(n_jobs=n_jobs)(delayed(ergodic_back_test)(_offset) for _offset in tqdm(offsets))
else:
    for _offset in tqdm(offsets):
        ergodic_back_test(_offset)

file_name = f'{stg_name}_轮动资金曲线_{hold_period}_*.csv'  # 以策略文件名_后的部分开头，之后是SPOT或SWAP，再接着hold_period和offset
all_equity_list = glob(back_test_path + file_name)  # 将文件夹下所有对应的资金曲线文件找出来
all_equity_list = [i for i in all_equity_list if '轮动模拟器' not in i]

all_equity_df = pd.DataFrame()  # 定义空的资金曲线df
rtn_list = []  # 所有offset策略评价集合
sorted_offset = {}  # 用于图表的offset排序
for equity_file in all_equity_list:
    _offset = equity_file.split('_')[-1].replace('.csv', '')  # 从文件名称中解析出offset
    if int(_offset) not in offsets:
        continue
    # 读取资金曲线文件
    _df = pd.read_csv(equity_file, encoding='gbk', parse_dates=['candle_begin_time'])
    _df = _df[['candle_begin_time', '轮动涨跌幅', '轮动资金曲线']]
    _df.rename(columns={'轮动涨跌幅': f'轮动涨跌幅_{_offset}', '轮动资金曲线': f'轮动资金曲线_{_offset}'}, inplace=True)

    # 将各个offset资金曲线合并到all_equity_df中
    if all_equity_df.empty:  # 数据为空，直接拷贝读取的资金曲线文件即可
        all_equity_df = _df.copy()
    else:  # 当前已经存在资金曲线，则将新offset资金曲线合并到all_equity_df中
        all_equity_df = pd.merge(left=all_equity_df, right=_df, on='candle_begin_time', how='outer')

    # 策略评价
    _df['轮动资金曲线'] = _df[f'轮动资金曲线_{_offset}']
    _df['轮动涨跌幅'] = _df[f'轮动涨跌幅_{_offset}']
    rtn, _, _ = Func.strategy_evaluate(_df, net_col='轮动资金曲线', pct_col='轮动涨跌幅')
    rtn = rtn.T
    rtn['offset'] = _offset
    rtn_list.append(rtn)
    sorted_offset[f'轮动资金曲线_{_offset}'] = _df.iloc[-1]['轮动资金曲线']

# 合并每个offset的策略评价信息
rtn_df = pd.concat(rtn_list, ignore_index=True)
rtn_df.set_index('offset', inplace=True)
rtn_df.sort_values('累积净值', ascending=False, inplace=True)
print(rtn_df.to_markdown())  # 输出各个offset策略评价信息

# 重新排序
all_equity_df.sort_values('candle_begin_time', inplace=True)
all_equity_df.reset_index(inplace=True, drop=True)

# 对空的涨跌幅填充0
pct_cols = [_ for _ in all_equity_df.columns if '涨跌幅' in _]
all_equity_df.loc[:, pct_cols] = all_equity_df[pct_cols].fillna(value=0)

# 对空的资金曲线向下填充，无法向下填充的填充为1
equity_cols = [_ for _ in all_equity_df.columns if '轮动资金曲线' in _]
all_equity_df.loc[:, equity_cols] = all_equity_df[equity_cols].fillna(method='ffill')
all_equity_df.loc[:, equity_cols] = all_equity_df[equity_cols].fillna(value=1)

# 计算所有offset的均值，获取账户总的净值资金曲线
all_equity_df['轮动资金曲线'] = all_equity_df[equity_cols].mean(axis=1)
# 计算账户涨跌幅，通过账户净值反推
all_equity_df['轮动涨跌幅'] = pd.DataFrame([1] + all_equity_df['轮动资金曲线'].to_list()).pct_change()[0].iloc[1:].to_list()
save_path = os.path.join(back_test_path, f'{stg_name}_轮动资金曲线_{hold_period}.csv')
all_equity_df.to_csv(save_path, encoding='gbk', index=False)

# 对所有offset资金曲线进行评价
rtn, year_return, month_return = Func.strategy_evaluate(all_equity_df, net_col='轮动资金曲线', pct_col='轮动涨跌幅')
save_path = os.path.join(back_test_path, f'{stg_name}_策略评价_{hold_period}.csv')
rtn.to_csv(save_path, encoding='gbk')
print('\n\n所有offset综合策略评价\n', rtn, '\n\n所有offset综合分年收益率：\n', year_return, '\n\n所有offset综合分月收益率：\n', month_return)

# =====画图
BTC = pd.read_csv(swap_path + 'BTC-USDT.csv', encoding='gbk', parse_dates=['candle_begin_time'], skiprows=1)
BTC['BTC涨跌幅'] = BTC['close'].pct_change()
all_equity_df = pd.merge(left=all_equity_df, right=BTC[['candle_begin_time', 'BTC涨跌幅']], on=['candle_begin_time'], how='left')
all_equity_df['BTC涨跌幅'].fillna(value=0, inplace=True)
all_equity_df['BTC资金曲线'] = (all_equity_df['BTC涨跌幅'] + 1).cumprod()

# 生成画图数据字典，可以画出所有offset资金曲线以及各个offset资金曲线
data_dict = {'轮动资金曲线': '轮动资金曲线', 'BTC资金曲线': 'BTC资金曲线'}
# 对各个offset的表现，按照净值进行从大到小排序
sorted_offset = sorted(sorted_offset.items(), key=lambda d: d[1], reverse=True)
# 遍历所有offset字段，将各个offset资金曲线的数据进行配置，方便后面画图
for col, v in sorted_offset:
    data_dict['offset_' + col.split('_')[1]] = col
pic_title = 'factor:%s_nv:%s_pro:%s_risk:%s' % (factor_list, rtn.at['累积净值', 0], rtn.at['年化收益', 0], rtn.at['最大回撤', 0])
# 调用画图函数
Eva.draw_equity_curve_plotly1(all_equity_df, data_dict=data_dict, date_col='candle_begin_time', right_axis={'轮动最大回撤': 'dd2here'}, title=pic_title)
